#include "sdk.h"
#include "M_Code_StyledTextCtrl.h"
#include <wx/listbook.h>
#include <wx/radiobox.h>
#include "newfromtemplatedlg.h"
#include "projecttemplateloader.h"

struct ListItemData {
  ListItemData( ProjectTemplateLoader* t = nullptr, cbWizardPlugin* p = nullptr, int i = 0 ) : pt( t ), plugin( p ), wizPluginIndex( i ) {}
  ListItemData( const ListItemData& rhs ) : pt( rhs.pt ), plugin( rhs.plugin ) {}
  ProjectTemplateLoader* pt;
  cbWizardPlugin* plugin;
  int wizPluginIndex;
};

static int idEditWizardScript = wxNewId();
static int idDiscardWizardScript = wxNewId();
static int idInfoWizardScript = wxNewId();
static int idEditGlobalWizardScript = wxNewId();

BEGIN_EVENT_TABLE( NewFromTemplateDlg, wxScrollingDialog )
  EVT_UPDATE_UI( -1, NewFromTemplateDlg::OnUpdateUI )
  EVT_LIST_ITEM_RIGHT_CLICK( XRCID( "listProjects" ), NewFromTemplateDlg::OnListRightClick )
  EVT_LIST_ITEM_ACTIVATED( XRCID( "listProjects" ), NewFromTemplateDlg::OnListActivate )
  EVT_CHOICE( XRCID( "cmbProjectCategories" ), NewFromTemplateDlg::OnCategoryChanged )
  EVT_LIST_ITEM_RIGHT_CLICK( XRCID( "listTargets" ), NewFromTemplateDlg::OnListRightClick )
  EVT_LIST_ITEM_ACTIVATED( XRCID( "listTargets" ), NewFromTemplateDlg::OnListActivate )
  EVT_CHOICE( XRCID( "cmbTargetCategories" ), NewFromTemplateDlg::OnCategoryChanged )
  EVT_LIST_ITEM_RIGHT_CLICK( XRCID( "listFiles" ), NewFromTemplateDlg::OnListRightClick )
  EVT_LIST_ITEM_ACTIVATED( XRCID( "listFiles" ), NewFromTemplateDlg::OnListActivate )
  EVT_CHOICE( XRCID( "cmbFileCategories" ), NewFromTemplateDlg::OnCategoryChanged )
  EVT_LIST_ITEM_RIGHT_CLICK( XRCID( "listCustoms" ), NewFromTemplateDlg::OnListRightClick )
  EVT_LIST_ITEM_ACTIVATED( XRCID( "listCustoms" ), NewFromTemplateDlg::OnListActivate )
  EVT_CHOICE( XRCID( "cmbCustomCategories" ), NewFromTemplateDlg::OnCategoryChanged )
  EVT_MENU( idEditWizardScript, NewFromTemplateDlg::OnEditScript )
  EVT_MENU( idDiscardWizardScript, NewFromTemplateDlg::OnDiscardScript )
  EVT_MENU( idEditGlobalWizardScript, NewFromTemplateDlg::OnEditGlobalScript )
  EVT_RADIOBOX( XRCID( "rbView" ), NewFromTemplateDlg::OnViewChange )
  EVT_MENU( idInfoWizardScript, NewFromTemplateDlg::OnHelp )
END_EVENT_TABLE()

NewFromTemplateDlg::NewFromTemplateDlg( TemplateOutputType initial, const wxArrayString& user_templates ) : m_Template( nullptr ), m_pWizard( nullptr ), m_WizardIndex( -1 ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/配置向导.xrc" );
  if( !xml_res->Load( resPath ) )  {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return;
  }
  if( !xml_res->LoadObject( this, nullptr, _T( "dlgNewFromTemplate" ), _T( "wxScrollingDialog" ) ) ) {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( _T( "资源丢失" ) );
    return;
  }
  xml_File_lock = true;
  m_Wizards = Manager::Get()->GetPluginManager()->GetOffersFor( ptWizard );
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  SetSettingsIconsStyle( lb->GetListView(), sisNoIcons );
  XRCCTRL( *this, "listProjects", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listProjects", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listTargets", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listTargets", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listFiles", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listFiles", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listCustoms", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listCustoms", wxListCtrl )->SetImageList( new wxImageList( 32, 32 ), wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "rbView", wxRadioBox )->SetSelection( Manager::Get()->GetConfigManager( _T( "new_from_template" ) )->ReadInt( _T( "/view" ), 0 ) );
  ChangeView();
  BuildCategories();
  BuildList();
  XRCCTRL( *this, "lstUser", wxListBox )->Clear();
  for( unsigned int i = 0; i < user_templates.GetCount(); ++i ) {
    XRCCTRL( *this, "lstUser", wxListBox )->Append( user_templates[i] );
  }
  lb->SetSelection( ( int )initial );
}

NewFromTemplateDlg::~NewFromTemplateDlg() {
  delete XRCCTRL( *this, "listProjects", wxListCtrl )->GetImageList( wxIMAGE_LIST_NORMAL );
  delete XRCCTRL( *this, "listProjects", wxListCtrl )->GetImageList( wxIMAGE_LIST_SMALL );
  delete XRCCTRL( *this, "listTargets", wxListCtrl )->GetImageList( wxIMAGE_LIST_NORMAL );
  delete XRCCTRL( *this, "listTargets", wxListCtrl )->GetImageList( wxIMAGE_LIST_SMALL );
  delete XRCCTRL( *this, "listFiles", wxListCtrl )->GetImageList( wxIMAGE_LIST_NORMAL );
  delete XRCCTRL( *this, "listFiles", wxListCtrl )->GetImageList( wxIMAGE_LIST_SMALL );
  delete XRCCTRL( *this, "listCustoms", wxListCtrl )->GetImageList( wxIMAGE_LIST_NORMAL );
  delete XRCCTRL( *this, "listCustoms", wxListCtrl )->GetImageList( wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listProjects", wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listProjects", wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listTargets",  wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listTargets",  wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listFiles",    wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listFiles",    wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_SMALL );
  XRCCTRL( *this, "listCustoms",  wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_NORMAL );
  XRCCTRL( *this, "listCustoms",  wxListCtrl )->SetImageList( nullptr, wxIMAGE_LIST_SMALL );
  ClearList();
}

void NewFromTemplateDlg::ClearList() {
  ClearListFor( XRCCTRL( *this, "listProjects", wxListCtrl ) );
  ClearListFor( XRCCTRL( *this, "listTargets", wxListCtrl ) );
  ClearListFor( XRCCTRL( *this, "listFiles", wxListCtrl ) );
  ClearListFor( XRCCTRL( *this, "listCustoms", wxListCtrl ) );
}

void NewFromTemplateDlg::ClearListFor( wxListCtrl* list ) {
  if( !list ) {
    return;
  }
  for( int i = 0; i < list->GetItemCount(); ++i ) {
    ListItemData* data = ( ListItemData* )list->GetItemData( i );
    delete data;
  }
  list->ClearAll();
}

void NewFromTemplateDlg::BuildCategories() {
  BuildCategoriesFor( totProject, XRCCTRL( *this, "cmbProjectCategories", wxChoice ) );
  BuildCategoriesFor( totTarget, XRCCTRL( *this, "cmbTargetCategories", wxChoice ) );
  BuildCategoriesFor( totFiles, XRCCTRL( *this, "cmbFileCategories", wxChoice ) );
  BuildCategoriesFor( totCustom, XRCCTRL( *this, "cmbCustomCategories", wxChoice ) );
}

void NewFromTemplateDlg::BuildCategoriesFor( TemplateOutputType otype, wxChoice* cat ) {
  if( !cat ) {
    return;
  }
  cat->Clear();
  cat->Append( _T( "<All categories>" ) );
  for( unsigned int i = 0; i < m_Wizards.GetCount(); ++i ) {
    cbWizardPlugin* plugin = ( cbWizardPlugin* )m_Wizards[i];
    for( int w = 0; w < plugin->GetCount(); ++w ) {
      if( plugin->GetOutputType( w ) != otype ) {
        continue;
      }
      if( cat->FindString( plugin->GetCategory( w ) ) == wxNOT_FOUND ) {
        cat->Append( plugin->GetCategory( w ) );
      }
    }
  }
  cat->SetSelection( 0 );
}

inline int wxCALLBACK SortTemplates( long item1, long item2, M_Code_unused long sortData ) {
  ListItemData* data1 = reinterpret_cast<ListItemData*>( item1 );
  ListItemData* data2 = reinterpret_cast<ListItemData*>( item2 );
  if( !data1 && data2 ) {
    return 1;
  }
  if( data1 && !data2 ) {
    return -1;
  }
  if( data1 && data2 ) {
    wxString name1 = data1->pt ? data1->pt->m_Title : data1->plugin->GetTitle( data1->wizPluginIndex );
    wxString name2 = data2->pt ? data2->pt->m_Title : data2->plugin->GetTitle( data2->wizPluginIndex );
    return name1.CompareTo( name2 );
  }
  return 0;
}

void NewFromTemplateDlg::BuildList() {
  BuildListFor( totProject, XRCCTRL( *this, "listProjects", wxListCtrl ), XRCCTRL( *this, "cmbProjectCategories", wxChoice ) );
  BuildListFor( totTarget, XRCCTRL( *this, "listTargets", wxListCtrl ), XRCCTRL( *this, "cmbTargetCategories", wxChoice ) );
  BuildListFor( totFiles, XRCCTRL( *this, "listFiles", wxListCtrl ), XRCCTRL( *this, "cmbFileCategories", wxChoice ) );
  BuildListFor( totCustom, XRCCTRL( *this, "listCustoms", wxListCtrl ), XRCCTRL( *this, "cmbCustomCategories", wxChoice ) );
}

void NewFromTemplateDlg::BuildListFor( TemplateOutputType otype, wxListCtrl* list, const wxChoice* cat ) {
  if( !list || !cat ) {
    return;
  }
  ClearListFor( list );
  list->GetImageList( wxIMAGE_LIST_NORMAL )->RemoveAll();
  list->GetImageList( wxIMAGE_LIST_SMALL )->RemoveAll();
  wxBitmap bmp;
  bool all = cat->GetSelection() == 0;
  for( unsigned int i = 0; i < m_Wizards.GetCount(); ++i ) {
    cbWizardPlugin* plugin = ( cbWizardPlugin* )m_Wizards[i];
    for( int w = 0; w < plugin->GetCount(); ++w ) {
      if( plugin->GetOutputType( w ) != otype ) {
        continue;
      }
      if( all || plugin->GetCategory( w ).Matches( cat->GetStringSelection() ) ) {
        int iconIndex;
        if( plugin->GetBitmap( w ).Ok() ) {
          iconIndex = list->GetImageList( wxIMAGE_LIST_NORMAL )->Add( plugin->GetBitmap( w ) );
          list->GetImageList( wxIMAGE_LIST_SMALL )->Add( plugin->GetBitmap( w ) );
        } else
        { iconIndex = -2; }
        int index = list->InsertItem( 0, plugin->GetTitle( w ), iconIndex );
        if( index != -1 ) {
          list->SetItemData( index, ( wxIntPtr )( new ListItemData( nullptr, plugin, w ) ) );
          wxString script = ConfigManager::GetFolder( sdDataUser ) + _T( "/templates/wizard/" ) + plugin->GetScriptFilename( w );
          if( wxFileExists( script ) ) {
            list->SetItemTextColour( index, *wxRED );
          }
        }
      }
    }
  }
  list->SortItems( SortTemplates, ( wxIntPtr )0 );
}

wxListCtrl* NewFromTemplateDlg::GetVisibleListCtrl() {
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  size_t page = lb->GetSelection();
  switch( page ) {
    case 0:
      return XRCCTRL( *this, "listProjects", wxListCtrl );
    case 1:
      return XRCCTRL( *this, "listTargets", wxListCtrl );
    case 2:
      return XRCCTRL( *this, "listFiles", wxListCtrl );
    case 3:
      return XRCCTRL( *this, "listCustoms", wxListCtrl );
    default:
      return nullptr;
  }
}

wxChoice* NewFromTemplateDlg::GetVisibleCategory() {
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  size_t page = lb->GetSelection();
  switch( page ) {
    case 0:
      return XRCCTRL( *this, "cmbProjectCategories", wxChoice );
    case 1:
      return XRCCTRL( *this, "cmbTargetCategories", wxChoice );
    case 2:
      return XRCCTRL( *this, "cmbFileCategories", wxChoice );
    case 3:
      return XRCCTRL( *this, "cmbCustomCategories", wxChoice );
    default:
      return nullptr;
  }
}

TemplateOutputType NewFromTemplateDlg::GetVisibleOutputType() const {
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  size_t page = lb->GetSelection();
  switch( page ) {
    case 0:
      return totProject;
    case 1:
      return totTarget;
    case 2:
      return totFiles;
    case 3:
      return totCustom;
    default:
      return totProject;
  }
}

cbWizardPlugin* NewFromTemplateDlg::GetSelectedTemplate() {
  m_pWizard = nullptr;
  m_WizardIndex = -1;
  wxListCtrl* list = GetVisibleListCtrl();
  if( !list ) {
    return nullptr;
  }
  long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  if( index == -1 ) {
    return nullptr;
  }
  ListItemData* data = ( ListItemData* )list->GetItemData( index );
  m_pWizard = data->plugin;
  m_WizardIndex = data->wizPluginIndex;
  return m_pWizard;
}

bool NewFromTemplateDlg::SelectedUserTemplate() const {
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  size_t page = lb->GetSelection();
  return  page == lb->GetPageCount() - 1 &&
          XRCCTRL( *this, "lstUser", wxListBox )->GetSelection() != -1;
}

wxString NewFromTemplateDlg::GetSelectedUserTemplate() const {
  int sel = XRCCTRL( *this, "lstUser", wxListBox )->GetSelection();
  return sel != -1 ? XRCCTRL( *this, "lstUser", wxListBox )->GetString( sel ) : _T( "" );
}

void NewFromTemplateDlg::EditScript( const wxString& filename ) {
  if( filename.IsEmpty() ) {
    return;
  }
  wxString script = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/templates/wizard/" ) + filename;
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->Open( script );
  if( ed ) {
    ed->GetControl()->SetFocus();
    EndModal( wxID_CANCEL );
    return;
  }
  msging( _T( "无法打开脚本:\n" ) + script, _T( "错误" ), wxICON_ERROR, this );
}

void NewFromTemplateDlg::ChangeView() {
  int sel = XRCCTRL( *this, "rbView", wxRadioBox )->GetSelection();
  int style = sel == 0 ? wxLC_ICON : wxLC_LIST;
  XRCCTRL( *this, "listProjects", wxListCtrl )->SetSingleStyle( style );
  XRCCTRL( *this, "listTargets",  wxListCtrl )->SetSingleStyle( style );
  XRCCTRL( *this, "listFiles",    wxListCtrl )->SetSingleStyle( style );
  XRCCTRL( *this, "listCustoms",  wxListCtrl )->SetSingleStyle( style );
  BuildListFor( totTarget,  XRCCTRL( *this, "listTargets",  wxListCtrl ), XRCCTRL( *this, "cmbTargetCategories",  wxChoice ) );
  BuildListFor( totFiles,   XRCCTRL( *this, "listFiles",    wxListCtrl ), XRCCTRL( *this, "cmbFileCategories",    wxChoice ) );
  BuildListFor( totCustom,  XRCCTRL( *this, "listCustoms",  wxListCtrl ), XRCCTRL( *this, "cmbCustomCategories",  wxChoice ) );
}

void NewFromTemplateDlg::OnListRightClick( wxListEvent& event ) {
  wxListCtrl* list = GetVisibleListCtrl();
  if( !list ) {
    return;
  }
  wxMenu* menu = new wxMenu;
  ListItemData* data = reinterpret_cast<ListItemData*>( event.GetData() );
  if( data && data->plugin ) {
    menu->Append( idEditWizardScript, _T( "编辑此脚本" ) );
    wxString script = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/templates/wizard/" ) + data->plugin->GetScriptFilename( data->wizPluginIndex );
    if( wxFileExists( script ) ) {
      menu->Append( idDiscardWizardScript, _T( "放弃对此脚本的修改" ) );
      menu->Append( idInfoWizardScript, _T( "这个脚本为什么标红了?" ) );
    }
  }
  menu->Append( idEditGlobalWizardScript, _T( "编辑全局注册脚本" ) );
  list->PopupMenu( menu );
  delete menu;
}

void NewFromTemplateDlg::OnListActivate( M_Code_unused wxListEvent& event ) {
  EndModal( wxID_OK );
}

void NewFromTemplateDlg::OnCategoryChanged( M_Code_unused wxCommandEvent& event ) {
  BuildListFor( GetVisibleOutputType(), GetVisibleListCtrl(), GetVisibleCategory() );
}

void NewFromTemplateDlg::OnEditScript( M_Code_unused wxCommandEvent& event ) {
  wxListCtrl* list = GetVisibleListCtrl();
  if( !list ) {
    return;
  }
  long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  if( index == -1 ) {
    return;
  }
  ListItemData* data = ( ListItemData* )list->GetItemData( index );
  cbWizardPlugin* wiz = data->plugin;
  EditScript( wiz->GetScriptFilename( data->wizPluginIndex ) );
}

void NewFromTemplateDlg::OnDiscardScript( M_Code_unused wxCommandEvent& event ) {
  wxListCtrl* list = GetVisibleListCtrl();
  if( !list ) {
    return;
  }
  long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  if( index == -1 ) {
    return;
  }
  ListItemData* data = ( ListItemData* )list->GetItemData( index );
  if( !data ) {
    return;
  }
  wxString script = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/templates/wizard/" ) + data->plugin->GetScriptFilename( data->wizPluginIndex );
  if( wxFileExists( script ) ) {
    if( msging( _T( "是否确实要放弃对此脚本的所有本地修改?" ),
                _T( "审查" ), wxICON_QUESTION | wxYES_NO, this ) == wxID_YES ) {
      if( wxRemoveFile( script ) ) {
        list->SetItemTextColour( index, wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
      }
    }
  }
}

void NewFromTemplateDlg::OnEditGlobalScript( M_Code_unused wxCommandEvent& event ) {
  msging( _T( "Any changes you make to the global wizard registration script will take effect after you restart Code::Blocks." ),
          _T( "Information" ), wxICON_INFORMATION, this );
  EditScript( _T( "config.script" ) );
}

void NewFromTemplateDlg::OnViewChange( M_Code_unused wxCommandEvent& event ) {
  ChangeView();
}

void NewFromTemplateDlg::OnHelp( M_Code_unused wxCommandEvent& event ) {
  msging( _T( "自定义脚本位于:\n" ) +
          ConfigManager::GetFolder( sdDataUser ) + _T( "/templates/wizard/" ),
          _T( "Help" ),
          wxICON_INFORMATION, this );
}

void NewFromTemplateDlg::OnUpdateUI( M_Code_unused wxUpdateUIEvent& event ) {
  wxListbook* lb = XRCCTRL( *this, "nbMain", wxListbook );
  size_t page = lb->GetSelection();
  XRCCTRL( *this, "wxID_OK", wxButton )->Enable( ( page != lb->GetPageCount() - 1 && GetSelectedTemplate() ) || SelectedUserTemplate() );
}

void NewFromTemplateDlg::EndModal( int retCode ) {
  int sel = XRCCTRL( *this, "rbView", wxRadioBox )->GetSelection();
  Manager::Get()->GetConfigManager( _T( "new_from_template" ) )->Write( _T( "/view" ), ( int )sel );
  GetSelectedTemplate();
  wxScrollingDialog::EndModal( retCode );
}
